"""
提供適應本系統的ElasticSearch 底層Api介面，包含：
1. 搜索es_search_interface
2. 單筆寫入es_insert_interface
3. 批量寫入es_bulk_insert_interface
"""
import logging
import copy
import os
import json
import requests
import random
from django.conf import settings

INDEX_ALIAS_PREFIX = 'log_'

_LOGGER = logging.getLogger(__name__)


def _get_es_url(cluster, index):
    """ 未來實現負載均衡與客戶端當機時異常處理 """
    es_cluster_client_urls = settings.ES_CLUSTER_CLIENT_URLS
    assert cluster in es_cluster_client_urls
    es_cluster_client_urls = es_cluster_client_urls.get(cluster)
    es_base_url = _get_url(es_cluster_client_urls)
    return os.path.join(es_base_url, index, '_doc')


def _get_url(es_cluster_client_urls):
    assert len(es_cluster_client_urls) > 0
    candidate_url = []
    for url, weight in es_cluster_client_urls.items():
        candidate_url += [url] * weight
    es_base_url = random.choice(candidate_url)
    return es_base_url


def _get_alias_index(index):
    indices = index.split(',')
    indices = [INDEX_ALIAS_PREFIX + idx for idx in indices]
    return ','.join(indices)


def es_search_interface(index, es_dsl, cluster='maestro'):
    """
    ElasticSearch 查詢統一入口
    :param index: 目標索引
    :param es_dsl: 搜索dsl語法，json結構
    :param cluster: 目標集群，集群遷移過渡期保留，後面廢除
    :return: 查詢結果
    """
    index = _get_alias_index(index)
    es_base_url = _get_es_url(cluster, index)
    es_search_url = os.path.join(es_base_url, '_search')
    response = requests.post(es_search_url, data=json.dumps(es_dsl), headers={'Content-Type': 'application/json'})
    if response.status_code != 200:
        raise Exception('search error')
    resp_json = json.loads(response.content)
    if 'hits' not in resp_json:
        raise Exception('search error')
    return resp_json


def es_insert_interface(index, data, cluster='maestro'):
    """
    ElasticSearch 單條數據寫入統一入口
    :param index: 目標索引
    :param data: 一條待寫入數據
    '_id'為保留字段，為es紀錄的唯一識別號。可於每條數據指定'_id'，若無指定'_id'則'_id'隨機生成。(注：’_id‘為字符組)
    :param cluster: 目標集群，集群遷移過渡期保留，後面廢除
    :return: es response
    """
    es_base_url = _get_es_url(cluster, index)
    if '_id' in data:
        es_base_url = os.path.join(es_base_url, '_'.join([str(data['_event_id']), str(data['_id'])]))
        del data['_id']
        response = requests.put(es_base_url, data=json.dumps(data), headers={'Content-Type': 'application/json'})
    else:
        response = requests.post(es_base_url, data=json.dumps(data), headers={'Content-Type': 'application/json'})
    if response.status_code not in [200, 201]:
        raise Exception('out to es fail')
    return response


def _format_bulk_data(index, bulk_data):
    formatted_bulk_data = ''
    for each in bulk_data:
        action_meta = {'index': {'_index': index, '_type': '_doc'}}
        if '_id' in each and each['_id'] is not None:
            action_meta['index']['_id'] = '_'.join([str(each['_event_id']), str(each['_id'])])
            each = copy.copy(each)
            del each['_id']
        formatted_bulk_data = formatted_bulk_data + json.dumps(action_meta) + '\n' + json.dumps(each) + '\n'
    return formatted_bulk_data


def es_bulk_insert_interface(index, bulk_data, cluster='maestro'):
    """
    ElasticSearch 批量數據寫入統一入口
    :param index: 目標索引
    :param bulk_data: 批量數據，一個json數組，每一個元素為一條待寫入數據。
    '_id'為保留字段，為es紀錄的唯一識別號。可於每條數據指定'_id'，若無指定'_id'則'_id'隨機生成。(注：’_id‘為字符組)
    範例: [{"_id": 123, "field_1": "field_1_value"}, {"field_1": "field_1_value_2"}]
    :param cluster: 目標集群，集群遷移過渡期保留，後面廢除
    :return: es response
    """
    es_base_url = _get_es_url(cluster, index)
    es_bulk_insert_url = os.path.join(es_base_url, '_bulk?pretty')
    formatted_bulk_data = _format_bulk_data(index, bulk_data)
    response = requests.put(es_bulk_insert_url, data=formatted_bulk_data, headers={'Content-Type': 'application/json'})
    if response.status_code not in [200, 201]:
        raise Exception('out to es fail, response: {}'.format(response.content))
    return response
